// -*- tab-width:3; intent-tabs-mode:nil;  -*-
using Mono.Unix;
using System;
using Gtk;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Threading;

using Tomboy;

namespace Tomboy.UrlTitle
{
public class UrlTitleNoteAddin : NoteAddin
{
   NoteTag url_tag;
   Gtk.TextMark click_mark;
   public override void Initialize ()
   {
      url_tag = (NoteTag) Note.TagTable.Lookup ("link:url");
   }

   public override void Shutdown ()
   {

   }

   public override void OnNoteOpened ()
   {
      click_mark = Buffer.CreateMark (null, Buffer.StartIter, true);

      Window.Editor.ButtonPressEvent += OnButtonPress;
      Window.Editor.PopulatePopup += OnPopulatePopup;
      Window.Editor.PopupMenu += OnPopupMenu;
   }

   [GLib.ConnectBefore]
   void OnButtonPress (object sender, Gtk.ButtonPressEventArgs args)
   {
      int x, y;

      Window.Editor.WindowToBufferCoords (Gtk.TextWindowType.Text,
                                          (int) args.Event.X,
                                          (int) args.Event.Y,
                                          out x,
                                          out y);
      Gtk.TextIter click_iter = Window.Editor.GetIterAtLocation (x, y);

      // Move click_mark to click location
      Buffer.MoveMark (click_mark, click_iter);

      // Continue event processing
      args.RetVal = false;
   }

   void OnPopulatePopup (object sender, Gtk.PopulatePopupArgs args)
   {
      Gtk.TextIter click_iter = Buffer.GetIterAtMark (click_mark);
      if (click_iter.HasTag (url_tag) || click_iter.EndsTag (url_tag)) {
         Gtk.MenuItem item;

         item = new Gtk.SeparatorMenuItem ();
         item.Show ();
         args.Menu.Prepend (item);

         item = new Gtk.MenuItem ("Fetch Link _Title");
         item.Activated += FetchUrlActivated;
         item.Show ();
         args.Menu.Prepend (item);

      }
   }

   [GLib.ConnectBefore]
   void OnPopupMenu (object sender, Gtk.PopupMenuArgs args)
   {
      Gtk.TextIter click_iter = Buffer.GetIterAtMark (Buffer.InsertMark);
      Buffer.MoveMark (click_mark, click_iter);

      args.RetVal = false; // Continue event processing
   }

   delegate void AsyncFetcher(string url);

   void FetchUrlActivated (object sender, EventArgs args)
   {

      Gtk.TextIter click_iter = Buffer.GetIterAtMark (click_mark);

      Gtk.TextIter start, end;
      url_tag.GetExtents (click_iter, out start, out end);
      string url = start.GetText (end);
      url = url.Trim ();

      if (! url.StartsWith ("http"))
      {
         url = "http://" + url;
      }

      Buffer.CreateMark(url, end, true);

      AsyncFetcher fetcher = new AsyncFetcher(StartFetch);
      fetcher.BeginInvoke(url, null, null);
   }

   private void StartFetch(string url)
   {
      string title = FetchUrlTitleWithProgressBar(url);

      // Task thread.  Do not call GTK directly
      Gtk.Application.Invoke (delegate {
            Gtk.TextMark url_mark = Buffer.GetMark(url);
            if (url_mark != null)
            {
               Gtk.TextIter url_iter = Buffer.GetIterAtMark(url_mark);
               if ((title != null) && (title.Length > 0))
               {
                  Buffer.Insert(ref url_iter, string.Format(" [{0}]", title));
               }
               Buffer.DeleteMark(url);
            }
         });
   }

   private delegate void UpdateProgressHandler(double progress);

   private string FetchUrlTitleWithUpdates(string url, UpdateProgressHandler progressHandler)
   {
      try
      {

         HttpWebResponse response = (HttpWebResponse)HttpWebRequest.Create(url).GetResponse();
         byte[] buf = new byte[8192];

         Stream respStream = response.GetResponseStream();
         StringBuilder respBody = new StringBuilder();

         int count = 0;
         double total = 0.0;

         do
         {
            total += count = respStream.Read(buf, 0, buf.Length);

            if (count != 0)
               respBody.Append(Encoding.ASCII.GetString(buf, 0, count));

            if ((response.ContentLength != 0) && (progressHandler != null))
            {
               progressHandler(total / response.ContentLength);
            }

         }
         while (count > 0);

         // I should check the return type
         string body = respBody.ToString();

         int titleBegin = body.IndexOf("<title>",StringComparison.InvariantCultureIgnoreCase ) + "<title>".Length;
         int titleLength = body.IndexOf("</title>", StringComparison.InvariantCultureIgnoreCase) - titleBegin;

         string title = body.Substring(titleBegin, titleLength);

         // Strip the newlines
         return Regex.Replace(title, @"[\r\n]", "").Trim();
      }
      catch (WebException webex)
      {
         Logger.Log("Could not retrieve " + url + " :" + webex.Message);
         return("");
      }
   }

   private string FetchUrlTitleWithProgressBar(string url)
   {
      Gtk.Window progressWindow = null;
      Gtk.ProgressBar pbar = null;

      // Task thread.  Do not call GTK directly
      Gtk.Application.Invoke (delegate {
            progressWindow = new Gtk.Window(Gtk.WindowType.Toplevel);
            progressWindow.AcceptFocus = false;
            pbar = new Gtk.ProgressBar();
            pbar.Show();
            progressWindow.Add(pbar);
            progressWindow.Title = "Fetching " + url;
            progressWindow.ShowAll();
            pbar.Fraction = 0;
         });

      string title = FetchUrlTitleWithUpdates(
         url,
         // passing as a handler a lambda which will in turn send the
         // process progress to Gtk.Application to update the progress
         // bar in the main Gtk loop.
         // Had I known C# could do this I would have picked it up years ago
         delegate(double progress){
               Gtk.Application.Invoke (delegate {
                     if  (progress > 0)
                     {
                        pbar.Fraction = progress;
                     }
                     else
                     {
                        pbar.Pulse();
                     }
               });
         });

      // Task thread.  Do not call GTK directly
      Gtk.Application.Invoke (delegate {
            progressWindow.Remove(pbar);
            progressWindow.Destroy();
         });

      return title;

   }



}

}

